package com.phiture.erp.produce.core.handler;

import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import com.phiture.erp.basic.api.pojo.resp.ErpMaterialInfoResp;
import com.phiture.erp.basic.api.service.ErpMaterialApi;
import com.phiture.erp.common.enums.ErpProductionTaskQualityCheckStatusEnum;
import com.phiture.erp.common.enums.ErpProductionTaskStatusEnum;
import com.phiture.erp.common.utils.ErpNoGenerator;
import com.phiture.erp.produce.core.pojo.dto.MrpBomInfoDTO;
import com.phiture.erp.produce.core.pojo.dto.MrpTaskDispatchDTO;
import com.phiture.erp.produce.core.service.*;
import com.phiture.erp.produce.dal.entity.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

import static com.phiture.erp.common.constants.ErpNoPrefixConstants.PRODUCTION_TASK_NO_PREFIX;

/**
 * @author YH
 * @date 2025-05-13-14:22
 */

@Slf4j
@RequiredArgsConstructor
@Component("mrp_produce_task")
public class MrpProduceTaskHandler extends BaseMrpTaskDispatchHandler implements MrpTaskDispatchHandler{

    private final ErpProductionTaskService erpProductionTaskService;
    private final ErpMaterialApi erpMaterialApi;
    private final ErpMaterialRequirementPlanService erpMaterialRequirementPlanService;
    private final ErpNoGenerator erpNoGenerator;
    private final ErpProduceRoutingService erpProduceRoutingService;
    private final BomService bomService;
    private final ErpProduceProcessService erpProduceProcessService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void taskExecute(MrpTaskDispatchDTO dto) {
        List<ErpMaterialRequirementPlanItemsDO> items = getErpMaterialRequirementPlanItems(dto.getMrpItemIds());
        Set<Long> materialIdSet = items.stream().map(ErpMaterialRequirementPlanItemsDO::getMaterialId).collect(Collectors.toSet());
        Set<Long> materialRequirementPlanIdSet = items.stream().map(ErpMaterialRequirementPlanItemsDO::getMaterialRequirementPlanId).collect(Collectors.toSet());
        Map<Long, ErpMaterialInfoResp> materialMap = erpMaterialApi.getMaterialMap(materialIdSet);
        Map<Long, String> requirementPlanMap = erpMaterialRequirementPlanService.getErpMaterialRequirementPlanMap(materialRequirementPlanIdSet);
        Map<Long, Long> produceRoutingMap = erpProduceRoutingService.getProduceRoutingMap(materialIdSet);
        for (ErpMaterialRequirementPlanItemsDO item : items) {
            ErpMaterialInfoResp material = materialMap.get(item.getMaterialId());
            String produceMainPlanNo = requirementPlanMap.get(item.getMaterialRequirementPlanId());
            Long produceRoutingId = produceRoutingMap.get(item.getMaterialId());
            Long taskId = processErpProductionTask(material, item, produceMainPlanNo, produceRoutingId);
            processErpProductionTaskMaterial(taskId, item.getMaterialId(), item.getActualQuantity());
            processErpProductionTaskProcesses(taskId, item.getStartDate(), produceRoutingId);
        }
        backfillItemsStatus(dto.getMrpItemIds());
    }

    private void processErpProductionTaskProcesses(Long taskId, LocalDate startDate, Long produceRoutingId) {
        // 获取工艺路线明细并检查是否为空
        List<ErpProduceRoutingDetailDO> routingDetails = erpProduceRoutingService.getProduceRoutingDetailListByRoutingId(produceRoutingId);
        if (CollUtil.isEmpty(routingDetails)) {
            return;
        }

        // 获取工序信息映射
        Set<Long> processIds = routingDetails.stream()
                .map(ErpProduceRoutingDetailDO::getProcessId)
                .collect(Collectors.toSet());
        Map<Long, ErpProduceProcessDO> processMap = erpProduceProcessService.getProcessMap(processIds);

        // 初始化任务工序列表
        List<ErpProductionTaskProcessesDO> taskProcesses = routingDetails.stream()
                .map(routing -> {
                    ErpProduceProcessDO process = processMap.get(routing.getProcessId());
                    ErpProductionTaskProcessesDO taskProcess = new ErpProductionTaskProcessesDO();
                    taskProcess.setProductionTaskId(taskId);
                    taskProcess.setProcessId(process.getId());
                    taskProcess.setProcessCode(process.getCodeNo());
                    taskProcess.setProcessName(process.getProcessName());
                    taskProcess.setSequence(routing.getSequenceNumber());
                    taskProcess.setLeadDay(routing.getLeadDay());
                    taskProcess.setCompletedQuantity(BigDecimal.ZERO);
                    taskProcess.setQualifiedQuantity(BigDecimal.ZERO);
                    taskProcess.setScrapQuantity(BigDecimal.ZERO);
                    taskProcess.setStatus(ErpProductionTaskStatusEnum.TO_BE_PRODUCED.getCode());
                    taskProcess.setQualityCheck(ErpProductionTaskQualityCheckStatusEnum.UNINSPECTED.getStatus());
                    taskProcess.setFinalProcess(routing.getFinalProcess());
                    return taskProcess;
                })
                .sorted(Comparator.comparing(ErpProductionTaskProcessesDO::getSequence))
                .collect(Collectors.toList());

        Map<Integer, List<ErpProductionTaskProcessesDO>> sequenceGroups = taskProcesses.stream()
                .collect(Collectors.groupingBy(ErpProductionTaskProcessesDO::getSequence));

        LocalDate currentEndDate = startDate;

        for (Integer sequence : sequenceGroups.keySet().stream().sorted().collect(Collectors.toList())) {
            List<ErpProductionTaskProcessesDO> sameSequenceTasks = sequenceGroups.get(sequence);
            LocalDate groupStartDate = (sequence == 1) ? startDate : currentEndDate;
            for (ErpProductionTaskProcessesDO task : sameSequenceTasks) {
                LocalDate endDate = groupStartDate.plusDays(task.getLeadDay());
                task.setStartDate(groupStartDate);
                task.setEndDate(endDate);
                if (endDate.isAfter(currentEndDate)) {
                    currentEndDate = endDate;
                }
            }
        }
        erpProductionTaskService.createErpProductionTaskProcessesFromMrp(taskProcesses);
    }

    private void processErpProductionTaskMaterial(Long taskId, Long materialId, BigDecimal actualQuantity){
        List<MrpBomInfoDTO> materialBomList = bomService.getBom(materialId);
        if(CollUtil.isEmpty(materialBomList)){
            return;
        }

        // 获取工序信息映射
        Set<Long> processIds = materialBomList.stream()
                .map(MrpBomInfoDTO::getProcessId)
                .collect(Collectors.toSet());
        Map<Long, ErpProduceProcessDO> processMap = erpProduceProcessService.getProcessMap(processIds);


        List<ErpProductionTaskMaterialDO> taskMaterialList = materialBomList.stream().map(bomDTO -> {
            ErpProductionTaskMaterialDO taskMaterialDO = new ErpProductionTaskMaterialDO();
            taskMaterialDO.setProductionTaskId(taskId);

            taskMaterialDO.setMaterialId(bomDTO.getMaterialId());
            taskMaterialDO.setMaterialCode(bomDTO.getMaterialBarCode());
            taskMaterialDO.setMaterialName(bomDTO.getMaterialName());
            taskMaterialDO.setMaterialSpec(bomDTO.getMaterialStandard());
            taskMaterialDO.setMaterialUnit(bomDTO.getMaterialUnitName());
            taskMaterialDO.setPlannedQuantity(actualQuantity.multiply(bomDTO.getConsumeFixQty()));
            taskMaterialDO.setBackFlushFlag(bomDTO.isBackFlushFlag());
            taskMaterialDO.setDefaultWarehouseId(bomDTO.getDefaultWarehouseId());
            if(bomDTO.getProcessId() != null){
                ErpProduceProcessDO processDO = processMap.get(bomDTO.getProcessId());
                taskMaterialDO.setProcessId(bomDTO.getProcessId());
                taskMaterialDO.setProcessName(processDO.getProcessName());
            }
            return taskMaterialDO;
        }).collect(Collectors.toList());
        erpProductionTaskService.createErpProductionTaskMaterialFromMrp(taskMaterialList);
    }

    private Long processErpProductionTask(ErpMaterialInfoResp material, ErpMaterialRequirementPlanItemsDO item, String productMainPlanNo, Long produceRoutingId){
        ErpProductionTaskDO taskDO = new ErpProductionTaskDO();
        taskDO.setProductionTaskNo(erpNoGenerator.generate(PRODUCTION_TASK_NO_PREFIX));
        taskDO.setProductMainPlanNo(productMainPlanNo);
        taskDO.setMaterialId(material.getId());
        taskDO.setMaterialName(material.getName());
        taskDO.setMaterialSpec(material.getStandard());
        taskDO.setMaterialUnit(material.getUnitName());
        taskDO.setMaterialNumber(material.getBarCode());
        taskDO.setPlannedQuantity(item.getPlanQuantity());
        taskDO.setActualQuantity(BigDecimal.ZERO);
        taskDO.setProduceRoutingId(produceRoutingId);
        taskDO.setStatus(ErpProductionTaskStatusEnum.TO_BE_PRODUCED.getCode());
        taskDO.setStartDate(item.getStartDate());
        taskDO.setEndDate(item.getEndDate());
        taskDO.setCreator(String.valueOf(Objects.requireNonNull(SecurityFrameworkUtils.getLoginUser()).getId()));
        taskDO.setUpdater(String.valueOf(Objects.requireNonNull(SecurityFrameworkUtils.getLoginUser()).getId()));
        erpProductionTaskService.createErpProductionTaskFromMrp(taskDO);
        return taskDO.getId();
    }




}
